﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using PowerTriggers.ServiceReference;
using System.Management;

namespace PowerTriggers
{
    internal partial class SelectTasksForm : Form
    {
        private IList<Task> selectedTasks;
        private IDictionary<TaskTypes, IList<TaskProperty>> defaultProperties;
        private DataGridViewComboBoxEditingControl comboBox;
        private readonly string testDescription;

        private SelectTasksForm()
        {
            InitializeComponent();
        }

        public SelectTasksForm(Task[] tasks, IDictionary<TaskTypes, IList<TaskProperty>> defaultProperties, string testDescription)
            : this()
        {
            if (tasks == null)
            {
                this.selectedTasks = new List<Task>();
            }
            else
            {
                selectedTasks = ApplySequenceOrder(tasks.ToList());
            }
            this.defaultProperties = defaultProperties;
            this.testDescription = testDescription;
        }

        internal IList<Task> SelectedTasks
        {
            get
            {
                return ApplySequenceOrder(this.selectedTasks);
            }
        }

        private void SelectTasksForm_Load(object sender, EventArgs e)
        {
            foreach (Control control in this.Controls.OfType<Button>())
            {
                if (control != buttonCancel)
                {
                    control.Enabled = Program.IsCurrentUserAdministrator;
                }
            }
            if (!Program.IsCurrentUserAdministrator)
            {
                dataGridViewSelect.ReadOnly = true;
            }

            editServices.Create(TaskTypes.Service, defaultProperties, selectedTasks);
            editDevices.Create(TaskTypes.Device, defaultProperties, selectedTasks);
            editStopProcess.Create(TaskTypes.StopProcess, defaultProperties, selectedTasks);
            editStartProcess.Create(TaskTypes.StartProcess, defaultProperties, selectedTasks);

            editServices.LoadData();

            RefreshLists();

            Cursor.Current = Cursors.Default;
        }

        private IList<Task> ApplySequenceOrder(IList<Task> unsortedList)
        {
            IList<Task> sortedList = unsortedList.OrderBy(task => task.Sequence).ToList();

            for (int sequence = 0; sequence < sortedList.Count; sequence++)
            {
                sortedList[sequence].Sequence = sequence;
            }

            return sortedList;
        }

        private void buttonOK_Click(object sender, EventArgs e)
        {
            this.DialogResult = System.Windows.Forms.DialogResult.OK;
        }

        private void tabControl1_Selecting(object sender, TabControlCancelEventArgs e)
        {
            foreach (var editControl in e.TabPage.Controls.OfType<EditTasksUserControl>())
            {
                editControl.LoadData();
            }
        }

        private void RefreshLists()
        {
            dataGridViewSelect.RowCount = selectedTasks.Count;
            dataGridViewSelect.Refresh();

            foreach (var editControl in GetAllEditTaskControls())
            {
                editControl.RefreshList();
            }
        }

        private EditTasksUserControl[] GetAllEditTaskControls()
        {
            return new EditTasksUserControl[4] { editServices, editDevices, editStopProcess, editStartProcess };
        }

        private void dataGridViewSelect_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
        {
            DataGridView dgv = (DataGridView)sender;

            if (e.RowIndex >= 0 && e.RowIndex < selectedTasks.Count)
            {
                Task task = selectedTasks[e.RowIndex];
                dgv.Rows[e.RowIndex].Tag = task;
                if (e.ColumnIndex == columnTaskType.Index)
                {
                    e.Value = EnumUtilities.ConvertEnumToText(task.TaskType);
                }
                else if (e.ColumnIndex == columnName.Index)
                {
                    e.Value = task.Description;
                }
                else if (e.ColumnIndex == columnAction.Index || e.ColumnIndex == columnAppliesTo.Index)
                {
                    DataGridViewComboBoxCell currentCell = (DataGridViewComboBoxCell)dgv.Rows[e.RowIndex].Cells[e.ColumnIndex];
                    TaskProperty property = GetColumnProperty(task, e.ColumnIndex);
                    if (currentCell.Tag == null || ((Task)currentCell.Tag) != task)
                    {
                        currentCell.Tag = task;
                        currentCell.Items.Clear();
                        if (property == null || property.Choices == null || property.Choices.Length == 0)
                        {
                            currentCell.ReadOnly = true;
                            currentCell.DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing;
                        }
                        else
                        {
                            currentCell.ReadOnly = false;
                            currentCell.DisplayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton;
                            currentCell.Items.AddRange(property.Choices);
                            e.Value = property.Choices.Where(ch => ch.ChoiceType == property.SelectedChoice).FirstOrDefault();
                        }
                    }
                    else
                    {
                        if (!currentCell.ReadOnly && property != null)
                        {
                            e.Value = property.Choices.Where(ch => ch.ChoiceType == property.SelectedChoice).FirstOrDefault();
                        }
                    }
                }
            }
        }

        private void dataGridViewSelect_CellValuePushed(object sender, DataGridViewCellValueEventArgs e)
        {
            if (e.RowIndex >= 0 && e.RowIndex < selectedTasks.Count)
            {
                if (e.ColumnIndex == columnAction.Index || e.ColumnIndex == columnAppliesTo.Index)
                {
                    Task task = selectedTasks[e.RowIndex];
                    TaskProperty property = GetColumnProperty(task, e.ColumnIndex);
                    TaskPropertyChoice choice;
                    if (e.Value == null)
                    {
                        choice = property.Choices.FirstOrDefault();
                    }
                    else
                    {
                        choice = (TaskPropertyChoice)comboBox.SelectedItem;
                    }
                    property.SelectedChoice = choice.ChoiceType;
                }
            }
        }

        private bool ShowSelectUsername(Task task, TaskPropertyChoice choice)
        {
            bool success = false;

            try
            {
                Cursor.Current = Cursors.WaitCursor;
                string username = string.Empty;
                string password = string.Empty;
                if (choice.AdditionalData != null)
                {
                    if (choice.AdditionalData is string[])
                    {
                        string[] additionalData = (string[])choice.AdditionalData;
                        if (additionalData.Length == 2)
                        {
                            username = additionalData[0];
                            password = additionalData[1];
                        }
                    }
                }

                using (SelectUserName form = new SelectUserName(username, password))
                {
                    DialogResult result = form.ShowDialog(this);
                    if (result == DialogResult.OK)
                    {
                        choice.AdditionalData = new string[2] { form.Username, form.Password };
                        success = true;
                    }
                }
            }
            catch (System.Exception ex)
            {
                Cursor.Current = Cursors.Default;
                MessageBox.Show(this, "Error: " + ex.Message, "Power Triggers", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }

            return success;
        }

        private bool ShowSelectTimePeriod(Task task, TaskPropertyChoice choice)
        {
            bool success = false;

            try
            {
                Cursor.Current = Cursors.WaitCursor;
                int? milliseconds = null;
                if (choice.AdditionalData != null)
                {
                    if (choice.AdditionalData is int)
                    {
                        milliseconds = (int)choice.AdditionalData;
                    }
                }

                using (SelectTimePeriod form = new SelectTimePeriod(milliseconds))
                {
                    DialogResult result = form.ShowDialog(this);
                    if (result == DialogResult.OK)
                    {
                        choice.AdditionalData = form.Milliseconds;
                        success = true;
                    }
                }
            }
            catch (System.Exception ex)
            {
                Cursor.Current = Cursors.Default;
                MessageBox.Show(this, "Error: " + ex.Message, "Power Triggers", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }

            return success;
        }

        private void buttonDeSelect_Click(object sender, EventArgs e)
        {
            if (Program.IsCurrentUserAdministrator)
            {
                List<int> selectedIndices = (from dgvc in dataGridViewSelect.SelectedCells.OfType<DataGridViewCell>()
                                             select dgvc.RowIndex).Distinct().ToList();
                List<Task> selectedTasks = (from i in selectedIndices
                                            select this.selectedTasks[i]).ToList();

                foreach (var task in selectedTasks)
                {
                    if (this.selectedTasks.Contains(task))
                    {
                        this.selectedTasks.Remove(task);
                    }
                    foreach (var editControl in GetAllEditTaskControls())
                    {
                        editControl.Include(task);
                    }
                }

                RefreshLists();
            }
        }

        private void dataGridViewSelect_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            DataGridView dgv = (DataGridView)sender;

            if (e.Button == System.Windows.Forms.MouseButtons.Left && e.RowIndex >= 0 && e.RowIndex < dgv.Rows.Count)
            {
                DataGridViewCell cell = dgv.Rows[e.RowIndex].Cells[e.ColumnIndex];
                if (!cell.ReadOnly && !cell.IsInEditMode)
                {
                    if (cell.EditType == typeof(DataGridViewComboBoxEditingControl))
                    {
                        dgv.BeginEdit(true);
                        DataGridViewComboBoxEditingControl comboboxEdit = (DataGridViewComboBoxEditingControl)dgv.EditingControl;
                        if (comboboxEdit != null)
                        {
                            comboboxEdit.DroppedDown = true;
                        }
                    }
                }
            }
        }

        private void editTasks_TasksSelected(object sender, SelectedTasksEventArgs e)
        {
            foreach (var task in e.SelectedTasks)
            {
                if (!selectedTasks.Contains(task))
                {
                    task.Sequence = selectedTasks.Count;
                    selectedTasks.Add(task);
                }
            }

            this.selectedTasks = ApplySequenceOrder(this.selectedTasks);

            RefreshLists();
        }

        private void buttonUp_Click(object sender, EventArgs e)
        {
            List<int> selectedIndices = (from dgvc in dataGridViewSelect.SelectedCells.OfType<DataGridViewCell>()
                                         select dgvc.RowIndex).Distinct().ToList();
            List<Task> userSelectedTasks = (from i in selectedIndices
                                            orderby i
                                            select this.selectedTasks[i]).ToList();

            foreach (var task in userSelectedTasks)
            {
                MoveTaskSequence(task, -1);
            }

            this.selectedTasks = ApplySequenceOrder(this.selectedTasks);

            RefreshLists();
        }

        private void buttonDown_Click(object sender, EventArgs e)
        {
            List<int> selectedIndices = (from dgvc in dataGridViewSelect.SelectedCells.OfType<DataGridViewCell>()
                                         select dgvc.RowIndex).Distinct().ToList();
            List<Task> userSelectedTasks = (from i in selectedIndices
                                            orderby i
                                            select this.selectedTasks[i]).ToList();

            foreach (var task in userSelectedTasks)
            {
                MoveTaskSequence(task, 1);
            }

            this.selectedTasks = ApplySequenceOrder(this.selectedTasks);

            RefreshLists();
        }

        private void MoveTaskSequence(Task task, int direction)
        {
            int oldSequence = task.Sequence;
            int swapWithIndex = this.selectedTasks.IndexOf(task) + direction;
            if (swapWithIndex >= 0 && swapWithIndex < this.selectedTasks.Count)
            {
                Task swapWithTask = this.selectedTasks[swapWithIndex];
                task.Sequence += direction;
                swapWithTask.Sequence -= direction;

                // now swap the cell selections on the grid
                DataGridViewRow dgvr1 = dataGridViewSelect.Rows.OfType<DataGridViewRow>().Where(dgvr => dgvr.Tag != null && ((Task)dgvr.Tag) == task).FirstOrDefault();
                DataGridViewRow dgvr2 = dataGridViewSelect.Rows.OfType<DataGridViewRow>().Where(dgvr => dgvr.Tag != null && ((Task)dgvr.Tag) == swapWithTask).FirstOrDefault();
                if (dgvr1 != null && dgvr2 != null)
                {
                    int[] selections1 = dgvr1.Cells.OfType<DataGridViewCell>().Where(cell => cell.Selected).Select(cell => cell.ColumnIndex).ToArray();
                    int[] selections2 = dgvr2.Cells.OfType<DataGridViewCell>().Where(cell => cell.Selected).Select(cell => cell.ColumnIndex).ToArray();

                    foreach (DataGridViewCell cell in dgvr2.Cells)
                    {
                        cell.Selected = selections1.Contains(cell.ColumnIndex);
                    }

                    foreach (DataGridViewCell cell in dgvr1.Cells)
                    {
                        cell.Selected = selections2.Contains(cell.ColumnIndex);
                    }
                }
            }
        }

        private void dataGridViewSelect_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            if ((e.Context & (DataGridViewDataErrorContexts.Formatting | DataGridViewDataErrorContexts.Display | DataGridViewDataErrorContexts.Commit | DataGridViewDataErrorContexts.Parsing | DataGridViewDataErrorContexts.LeaveControl)) != 0)
            {
                if (e.ColumnIndex == columnAction.Index)
                {
                    e.Cancel = true;
                }
                else if (e.ColumnIndex == columnAppliesTo.Index)
                {
                    e.Cancel = true;
                }
            }

            if (!e.Cancel && !e.ThrowException)
            {
                string msg;
                if (e.Exception == null)
                {
                    msg = "Unknown Error";
                }
                else
                {
                    msg = e.Exception.Message;
                }
                MessageBox.Show(this, "DataError. Context = " + e.Context.ToString() + ". Error=" + msg, "Power Triggers", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void editTasks_OtherClicked(object sender, EventArgs e)
        {
            try
            {
                EditTasksUserControl editControl = (EditTasksUserControl)sender;
                Cursor.Current = Cursors.WaitCursor;
                using (SelectOtherTaskForm form = new SelectOtherTaskForm(editControl.TaskType, editControl.DefaultProperties))
                {
                    DialogResult result = form.ShowDialog(this);
                    if (result == DialogResult.OK)
                    {
                        var task = form.Task;

                        if (!selectedTasks.Contains(task))
                        {
                            task.Sequence = selectedTasks.Count;
                            this.selectedTasks.Add(task);

                            editControl.Include(task);

                            this.selectedTasks = ApplySequenceOrder(this.selectedTasks);

                            RefreshLists();
                        }
                    }
                }
            }
            catch (System.Exception ex)
            {
                Cursor.Current = Cursors.Default;
                MessageBox.Show(this, "Error: " + ex.Message, "Power Triggers", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        private void buttonTest_Click(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;
                using (PowerTriggersWcfClient serviceClient = WcfClientFactory.OpenWcfClient())
                {
                    serviceClient.RunTest(this.testDescription, this.selectedTasks.ToArray());
                }
            }
            catch (System.Exception ex)
            {
                Cursor.Current = Cursors.Default;
                MessageBox.Show(this, "Error: " + ex.Message, "Power Triggers", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        private void dataGridViewSelect_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
        {
            if (this.comboBox != null)
            {
                //remove any previously added handlers        
                this.comboBox.SelectedIndexChanged -= new EventHandler(comboBox_SelectedIndexChanged);
                this.comboBox = null;
            }
            this.comboBox = e.Control as DataGridViewComboBoxEditingControl;
            if (this.comboBox != null)
            {
                this.comboBox.SelectedIndexChanged += new EventHandler(comboBox_SelectedIndexChanged);
            }
        }

        private void dataGridViewSelect_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            if (this.comboBox != null)
            {
                //remove any previously added handlers        
                this.comboBox.SelectedIndexChanged -= new EventHandler(comboBox_SelectedIndexChanged);
                this.comboBox = null;
            }
        }

        private void comboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (dataGridViewSelect.CurrentCell.ColumnIndex == columnAction.Index || dataGridViewSelect.CurrentCell.ColumnIndex == columnAppliesTo.Index)
            {
                DataGridViewComboBoxEditingControl comboBox = (DataGridViewComboBoxEditingControl)sender;
                Task task = selectedTasks[comboBox.EditingControlRowIndex];
                TaskProperty property = GetColumnProperty(task, dataGridViewSelect.CurrentCell.ColumnIndex);
                TaskPropertyChoice choice;
                if (comboBox.SelectedIndex < 0)
                {
                    choice = property.Choices.FirstOrDefault();
                }
                else
                {
                    choice = (TaskPropertyChoice)property.Choices[comboBox.SelectedIndex];
                }

                if (choice.AdditionalDataType == TaskPropertyAdditionalDataTypes.Username)
                {
                    if (!ShowSelectUsername(task, choice))
                    {
                        comboBox.EditingControlDataGridView.CancelEdit();
                    }
                }
                else if (choice.AdditionalDataType == TaskPropertyAdditionalDataTypes.Milliseconds)
                {
                    if (!ShowSelectTimePeriod(task, choice))
                    {
                        comboBox.EditingControlDataGridView.CancelEdit();
                    }
                }
            }
        }

        private TaskProperty GetColumnProperty(Task task, int columnIndex)
        {
            return columnIndex == columnAction.Index ? task.GetActionProperty() : task.GetAppliesToProperty();
        }
    }
}
